/**
 * Wihidum - Efficient Broker Hierarchy for Publish Subscribe Middleware
 *
 * License      :   Apache License 2.0
 * Home Page    :   http://code.google.com/p/wihidum/
 * Mailing List :   wihidum@googlegroups.com
 */

package lk.mrt.cse.wihidum.client;

import lk.mrt.cse.wihidum.util.*;
import org.apache.axiom.om.*;
import org.apache.axiom.soap.*;
import org.apache.axis2.AxisFault;
import org.apache.axis2.client.*;
import org.apache.axis2.context.MessageContext;
import org.apache.log4j.Logger;

/**
 * This exposes methods for publishers to send events to be pulished to a broker.
 * Publisher web services or client applications can use this class for publishing events.
 * Hence, this acts as an interface between the publishing applications and the broker service.
 */
public class PublicationClient {
    private static final Logger logger = Logger.getLogger(PublicationClient.class);

    private PublicationClientBean clientbean;

    /**
     * Default constructor
     *
     * @param clientBean Publication client bean
     */
    public PublicationClient(PublicationClientBean clientBean) {
        this.clientbean = clientBean;
    }

    /**
     * Publishes an event at a broker
     *
     * @return Response to the publication
     */
    public String publish() {
        try {
            logger.debug("Sending publication message");

            ServiceClient serviceClient = new ServiceClient();

            OperationClient opClient = serviceClient.createClient(ServiceClient.ANON_OUT_ONLY_OP);

            MessageContext outMsgCtx = new MessageContext();
            Options opts = outMsgCtx.getOptions();

            String brokerAddress = this.getBrokerAddress();
            if (brokerAddress != null) {
                opts.setTo(clientbean.getBrokerAddress());
                opts.setAction(WihidumConstants.Operations.PUBLISH);

                String SOAPVersion = opts.getSoapVersionURI();
                if (SOAPVersion == null)
                    SOAPVersion = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;

                SOAPEnvelope publicationEnvelope = this.createPublicationRequest(SOAPVersion);

                outMsgCtx.setEnvelope(publicationEnvelope);

                opClient.addMessageContext(outMsgCtx);
                opClient.execute(true);
                return "Publication process completed successfully";
            } else
                return "Broker services are not provided by Wihidum, Yet.";
        }
        catch (AxisFault ex) {
            ex.printStackTrace();
            return "Error in sending publication";
        }
    }

    private String getBrokerAddress() {
        if (BrokerList.isEmpty()) {
            String adminAddress = clientbean.getAdminAddress().getAddress();
            String publisherAddress = clientbean.getPublisherAddress().getAddress();
            BrokerRetrievalClient brokerRetrievalClient = new BrokerRetrievalClient();
            brokerRetrievalClient.retrieveBrokers(adminAddress, publisherAddress);
        }
        if (BrokerList.isEmpty()) {
            logger.info("Broker List is still empty. Therefore the broker address provide from the UI is taken.");
            return clientbean.getBrokerAddress().getAddress();
        } else {
            BrokerList.shuffleBrokerList();
            logger.info("Broker Address is taken from client's broker list.");
            return BrokerList.getTopBroker();
        }
    }


    /**
     * Creates the message for publication request
     *
     * @return Returns the OMElement which contains publication message
     */
    private SOAPEnvelope createPublicationRequest(String SOAPURI) {
        try {
            SOAPFactory soapFactory = null;
            if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
                soapFactory = OMAbstractFactory.getSOAP11Factory();
            else if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
                soapFactory = OMAbstractFactory.getSOAP12Factory();
            else throw new Exception("Unknown SOAP version");

            SOAPEnvelope soapEnvelope = soapFactory.getDefaultEnvelope();

            //declare the used namespaces in the SOAP Envelope
            OMNamespace wseventingNS = soapFactory.createOMNamespace(WSEventingConstants.EVENTING_NAMESPACE, WSEventingConstants.EVENTING_PREFIX);
            soapEnvelope.declareNamespace(wseventingNS);
            OMNamespace wsaNS = soapFactory.createOMNamespace(WSEventingConstants.ADDRESSING_NAMESPACE, WSEventingConstants.ADDRESSING_PREFIX);
            soapEnvelope.declareNamespace(wsaNS);
            OMNamespace wihidumNS = soapFactory.createOMNamespace(WSEventingConstants.WIHIDUM_NAMESPACE, WSEventingConstants.WHIDUM_NAMESPACE_PREFIX);
            soapEnvelope.declareNamespace(wihidumNS);

            OMFactory factory = OMAbstractFactory.getOMFactory();

            SOAPHeader soapHeader = soapEnvelope.getHeader();

            OMElement publicationHeader = factory.createOMElement("PublicationHeader", wihidumNS);

            OMElement replyTo = factory.createOMElement("ReplyTo", wihidumNS);
            OMElement address = factory.createOMElement("Address", wihidumNS);
            address.setText(clientbean.getPublisherAddress().getAddress());
            replyTo.addChild(address);
            publicationHeader.addChild(replyTo);

            OMElement topicElement = factory.createOMElement("Topic", wihidumNS);
            topicElement.setText(clientbean.getTopic());
            publicationHeader.addChild(topicElement);

            soapHeader.addChild(publicationHeader);

            SOAPBody soapBody = soapEnvelope.getBody();
            soapBody.setText(clientbean.getPublicationText());

            return soapEnvelope;
        }
        catch (Exception ex) {
            logger.error(ex);
            return null;
        }
    }

}
